%% ectremote.erl
%%      Provides simple functions to hide the message-passing when creating, or communicating 
%%      with object instances that run as separate processes (remote objects).
%% Copyright (C) 2008, 2009 Gábor Fehér
%%
%% This file is part of ECT.
%% 
%% ECT is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%% 
%% ECT is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%% GNU General Public License for more details.
%% 
%% You should have received a copy of the GNU General Public License
%% along with ECT.  If not, see <http://www.gnu.org/licenses/>.
%%

-module(ectremote).
-export([new/1, get/2, set/3, query_call/3, update_call/3, delete/1, copy/1]).

%% Function: result(Ref)
%% Purpose: waits for the response of a remote request
%% Arguments:
%%   Ref::ref() - reference id for the remote request
%% Returns:
%%   the value stored in the response
result(Ref) ->
    receive
        {ect_result, Ref, Res} -> 
            Res
% causes performance loss:
%    after
%        10000 ->
%            throw({ect_error, remote_timeout})
    end.

%% Function: new(Class)
%% Purpose: creates a new remote object of a class
%% Arguments:
%%   Class::atom() - the name of the class
%% Side-effect:
%%   A new process is started with a new object.
%% Returns:
%%   a new remote object of class (more precisely a process identifier to send messages to the new object)
new(Class) ->
    Class:rnew().

%% Function: get(Obj, Field)
%% Purpose: queries a field of a remote object
%% Arguments:
%%   Obj - the remote object (created with new)
%%   Field::atom() - the name of field to query
%% Returns:
%%   the value of Field in Obj
get(Obj, Field) ->
    Ref = make_ref(),
    Obj ! {get, Field, self(), Ref},
    result(Ref).

%% Function: set(Obj, Method, Value)
%% Purpose: sets new value for a field of a remote object
%% Arguments:
%%   Obj - the remote object (created with new)
%%   Field::atom() - the name of field to set
%%   Value - the new value to set
%% Side-effect:
%%   The field value is updated in the process of the object.
%% Returns:
%%   the response of the object to the set command
set(Obj, Field, Value) ->
    Obj ! {set, Field, Value}.

%% Function: query_call(Obj, Method, Params)
%% Purpose: issues a query call to a method of a remote object
%% Arguments:
%%   Obj - the remote object (created with new)
%%   Method::atom() - method to call
%%   Params - list of arguments to pass to the object
%% Returns:
%%   the response of the object to the query_call command
query_call(Obj, Method, Params) when is_list(Params) ->
    Ref = make_ref(),
    Obj ! {query_call, Method, Params, self(), Ref},
    result(Ref).

%% Function: update_call(Obj, Method, Params)
%% Purpose: issues an update call to a method of a remote object
%% Arguments:
%%   Obj - the remote object (created with new)
%%   Method::atom() - method to call
%%   Params - list of arguments to pass to the object
%% Side-effect:
%%   The fields of the remote object might get modified by the method.
%% Returns:
%%   the response of the object to the update_call command
update_call(Obj, Method, Params) when is_list(Params) ->
    Ref = make_ref(),
    Obj ! {update_call, Method, Params, self(), Ref},
    result(Ref).

%% Function: delete(Obj)
%% Purpose: deletes a remote object
%% Arguments:
%%   Obj - the remote object to delete 
%% Side-effect:
%%   The process of the object quits.
delete(Obj) ->
    Obj ! delete.

%% Function: copy(Obj)
%% Purpose: makes a non-remote copy of a remote object
%% Arguments:
%%   Obj - the remote object to copy
%% Returns:
%%   a "usual" data-based ECT object, with the same class and field values of the remote object
copy(Obj) ->
    Ref = make_ref(),
    Obj ! {copy, self(), Ref},
    result(Ref).
